package com.dyna.aop.aspect;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.dyna.aop.annotation.RequestRedisLimiter;
import com.dyna.constants.RedisKeyConstant;
import com.dyna.constants.StringPool;
import com.dyna.core.exception.CustomException;
import com.dyna.enums.CommonErrorEnum;
import com.dyna.utils.RdsUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class RequestRedisAspect {



    @Pointcut("@annotation(com.dyna.aop.annotation.RequestRedisLimiter)")
    public void redisLimiter() {

    }

    @Around("redisLimiter()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        Long currentTimeMillis = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        RequestRedisLimiter redisLimiter = signature.getMethod().getAnnotation(RequestRedisLimiter.class);
        // 缓存redis key后缀
        String prefix = joinPoint.getSignature().getName();
        String key = RedisKeyConstant.REDIS_LIMITER_KEY + prefix;

        String redisValue = RdsUtil.get(key);
        if (StrUtil.isBlank(redisValue)) {
            RdsUtil.set(key, 1 + StringPool.DASH + currentTimeMillis, 1, TimeUnit.MINUTES);
        }else {
            String[] split = redisValue.split(StringPool.DASH);
            Long count = Convert.convert(Long.class, split[0], 0L);
            Long firstTime = Convert.convert(Long.class, split[1], 0L);

            // 注解限制秒数
            Long limiterSec = Convert.convert(Long.class,redisLimiter.sec());
            Long limiterCount = Convert.convert(Long.class,redisLimiter.count());

            // 当前时间戳-第一次请求的时间戳小于=配置的时间戳，并且次数大于等于配置次数。则请求次数过于频繁
            if ((currentTimeMillis - firstTime)/1000 < limiterSec && count < limiterCount) {
                // 否则属于两次请求之间时间戳小于等于配置时间，并且次数小于配置次数，则访问次数加1
                Long incrCount = count + 1;
                String value = incrCount + StringPool.DASH + firstTime;
                RdsUtil.set(key, value, 1, TimeUnit.MINUTES);
            }else if ((currentTimeMillis - firstTime)/1000 >= limiterSec + 3) {
                // 限流3秒钟,当大于配置时间+3秒时，不管访问次数是大于配置次数还是小于配置次数，都清空缓存
                RdsUtil.del(key);
            }else {
                throw new CustomException(CommonErrorEnum.FREQUENCY_LIMIT.getMsg());
            }
        }
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return obj;
    }

}
